#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"

int main(){
    // 初始化llvm以及设置目标机
    llvm::InitializeNativeTarget();
    llvm::InitializeAllTargetInfos();
    llvm::InitializeAllTargets();
    llvm::InitializeAllTargetMCs();
    llvm::InitializeAllAsmParsers();
    llvm::InitializeAllAsmPrinters();
    // 初始化context以及module
    std::unique_ptr<llvm::LLVMContext> context = std::make_unique<llvm::LLVMContext>();
    std::unique_ptr<llvm::Module> mod = std::make_unique<llvm::Module>("test1.cpp", *context);
    // 获取目标三元组并设置
    auto targetTriple = llvm::sys::getDefaultTargetTriple();
    mod->setTargetTriple(targetTriple);
    // 获取目标机数据类型并设置
    std::string targetError;
    auto target = llvm::TargetRegistry::lookupTarget(targetTriple, targetError);
    auto cpu = "generic";
    auto features = "";
    llvm::TargetOptions options;
    auto relocationModel = llvm::Reloc::Model::PIC_;
    auto theTargetMachine = target->createTargetMachine(targetTriple, cpu, features, options, relocationModel);
    mod->setDataLayout(theTargetMachine->createDataLayout());
    // 创建struct
    llvm::StructType *testStruct = llvm::StructType::create(*context, "test");
    std::vector<llvm::Type *> elements;
    elements.push_back(llvm::Type::getInt32Ty(*context));
    elements.push_back(llvm::Type::getDoubleTy(*context));
    testStruct->setBody(elements);
    // 创建主函数以及builder, 需要使用的block
    auto mainFunctionType = llvm::FunctionType::get(llvm::Type::getInt32Ty(*context), false);
    auto mainFunction = llvm::Function::Create(mainFunctionType, llvm::GlobalValue::ExternalLinkage, "main", mod.get());
    auto entryBlock = llvm::BasicBlock::Create(*context, "", mainFunction);  //入口bloc
    std::unique_ptr<llvm::IRBuilder<>> builder = std::make_unique<llvm::IRBuilder<>>(entryBlock);
    // 在栈上分配一个结构体和一个int 32的空间
    auto addr1 = builder->CreateAlloca(mod->getTypeByName("test"), nullptr);  // struct test x;
    auto addr2 = builder->CreateAlloca(llvm::Type::getInt32Ty(*context), nullptr);  // int c;
    // 获取常量
    auto value1 = llvm::ConstantInt::get(llvm::Type::getInt32Ty(*context), 0);
    auto value2 = llvm::ConstantInt::get(llvm::Type::getInt32Ty(*context), 3);
    // 获取结构体内部字段的地址
    auto index_0 = llvm::ConstantInt::get(llvm::Type::getInt32Ty(*context), 0);
    auto index_1 = llvm::ConstantInt::get(llvm::Type::getInt32Ty(*context), 1);
    auto addr1_addra = builder->CreateGEP(mod->getTypeByName("test"), addr1, {index_0, index_0});  // x.a
    auto addr1_addrb = builder->CreateGEP(mod->getTypeByName("test"), addr1, {index_0, index_1});  // x.b
    // 将常量存储进 上面分配的空间
    builder->CreateStore(value1, addr1_addra);  // x.a=0
    builder->CreateStore(value2, addr1_addrb);  // x.b=3
    // 获取结构体内部字段的地址（这是为了模拟c语言的角度进行的操作）
    auto addr1_addra1 = builder->CreateGEP(mod->getTypeByName("test"), addr1, {index_0, index_0});  // x.a
    auto addr1_addrb1 = builder->CreateGEP(mod->getTypeByName("test"), addr1, {index_0, index_1});  // x.b
    // 从上述地址取值
    auto value3 = builder->CreateLoad(llvm::Type::getInt32Ty(*context), addr1_addra1);
    auto value4 = builder->CreateLoad(llvm::Type::getDoubleTy(*context), addr1_addrb1);
    // 进行转换操作
    auto value5 = builder->CreateFPToSI(value4, llvm::Type::getInt32Ty(*context));
    // 进行相加操作
    auto value6 = builder->CreateAdd(value5, value3);  // x.a+int(x.b)
    // 存
    builder->CreateStore(value6, addr2);
    builder->CreateRet(value6);  // return c
    // 输出汇编文件
    std::error_code errorCode;
    std::string filename = "main.s";  // 汇编文件名
    llvm::raw_fd_ostream dest(filename, errorCode, llvm::sys::fs::OF_None);
    llvm::legacy::PassManager pass;
    llvm::CodeGenFileType type = llvm::CGFT_AssemblyFile;  // 文件类型为汇编
    // llvm::CodeGenFileType type = llvm::CGFT_ObjectFile;  // 文件类型为可执行文件
    theTargetMachine->addPassesToEmitFile(pass, dest, nullptr, type);
    pass.run(*mod);
    dest.flush();
    mod->print(llvm::outs(), nullptr);  // 打印ir
}
